/*--------------------------------------------------------------------------
Modified from GNU Group's SDCC
Only for study purpose
---------------------------------------------------------------------------*/

#include "common.h"
#include "ccmem.h"
#include "Support\dbuf.h"

memmap *stack = NULL;           /* program stack                */
memmap *code = NULL;            /* code segment                 */
memmap *data = NULL;            /* internal data upto 128       */

/*-----------------------------------------------------------------*/
/* allocMap - allocates a memory map                               */
/*-----------------------------------------------------------------*/
memmap *
allocMap (unsigned sloc,        /* starting location    */
          const char *name,     /* 2 character name     */
          char codemap          /* this is code space   */
         )
{
	memmap *map;

	if (!(map = Safe_alloc (sizeof (memmap))))
	{
		printf ("out off memory");
		exit (1);
	}

	memset (map, 0, sizeof (memmap));

	map->codesp = codemap;
	map->sloc = sloc;
	map->sname = name;

	map->syms = NULL;
	return map;
}

void initMem ()
{
	/* stack space: 0   .. 199 */
	/* data space : 200 .. 399 */
	/* code space : 400 .. $$  */

	stack = allocMap(0, "stack", 0);
	data = allocMap(200, "data", 0);
	code = allocMap(400, "code", 0);
}

/*-----------------------------------------------------------------*/
/* allocGlobal - assigns the output segment to a global var        */
/*-----------------------------------------------------------------*/
void
allocGlobal (symbol * sym)
{

  /* symbol name is internal name  */
  if (!sym->level)              /* local statics can come here */
    sprintf (sym->rname,
              "%s%s", "_", sym->name);

  if (IS_FUNC(sym->type))
  {
	  SPEC_OCLS(sym->etype) = code;
	  if (!sym->cdef)
		allocIntoSeg (sym);	/* alloc now */
  }

  /* if this is a literal e.g. enumerated type */
  /* put it in the data segment & do nothing   */
  if (IS_LITERAL (sym->etype) ||
	  SPEC_SCLS(sym->etype) == S_DATA ||
	  SPEC_SCLS(sym->etype) == S_FIXED)
    {
	  SPEC_OCLS(sym->etype) = data;
	  allocIntoSeg (sym);	/* alloc now */

      return;
    }

  sym->islocal = 0;

  if (defaultOClass(sym))
	  allocIntoSeg (sym);

  return;
}


/*-----------------------------------------------------------------*/
/* allocLocal - allocate local variables                           */
/*-----------------------------------------------------------------*/
void
allocLocal (symbol * sym)
{
  /* generate an unique name */
  sprintf  (sym->rname,
            "%s%s_%s_%d_%d",
            "_",
            currFunc->name, sym->name, sym->level, sym->block);

  sym->islocal = 1;
  sym->localof = currFunc;

  /* if this is a static variable */
  if (IS_STATIC (sym->etype))
    {
      allocGlobal (sym);
      sym->allocreq = 1;
      return;
    }

  /* if volatile then */
  if (IS_VOLATILE (sym->etype))
    sym->allocreq = 1;

  /* alloc this variable on stack */
  sym->onStack = 1;
  SPEC_OCLS (sym->etype) = stack;		/* stack space */
  SPEC_STAK(sym->etype) = sym->stack = (stackPtr + 1);
  stackPtr += getSize(sym->type);

  allocIntoSeg (sym);	/* alloc now */
}

/*-----------------------------------------------------------------*/
/* allocVariables - creates decl & assign storage class for a v    */
/*-----------------------------------------------------------------*/
int
allocVariables (symbol * symChain)
{
  symbol *sym;
  symbol *csym;
  int saveLevel = 0;

  /* go thru the symbol chain   */
  for (sym = symChain; sym; sym = sym->next)
    {

      /* if this is a typedef then add it */
      /* to the typedef table             */
      if (IS_TYPEDEF (sym->etype))
        {
          /* check if the typedef already exists    */
          csym = findSym (TypedefTab, NULL, sym->name);
          if (csym && csym->level <= sym->level)
			  yylexerror ("duplidate typedef: %s", sym->name);

          SPEC_EXTR (sym->etype) = 0;
          addSym (TypedefTab, sym, sym->name, sym->level, sym->block, 0);
          continue;             /* go to the next one */
        }

      /* make sure it already exists */
      csym = findSymWithLevel (SymbolTab, sym);
      if (!csym || (csym && csym->level != sym->level))
        csym = sym;

      /* check the declaration */
      checkDecl (csym, 0);

      /* if this is a function or a pointer to a */
      /* function then do args processing        */
      if (funcInChain (csym->type))
        {
          processFuncArgs (csym);
        }

      /* if this is a extern variable then change the */
      /* level to zero temporarily                    */
      if (IS_EXTERN (csym->etype) || IS_FUNC (csym->type))
        {
          saveLevel = csym->level;
          csym->level = 0;
        }

      /* if this is a literal then it is an enumerated */
      /* type so need not allocate it space for it     */
      if (IS_LITERAL (sym->etype))
        continue;

      /* generate the actual declaration */
      if (csym->level)
        allocLocal (csym);
      else
        allocGlobal (csym);

      /* restore the level */
      if (IS_EXTERN (csym->etype) || IS_FUNC (csym->type))
        csym->level = saveLevel;
    }

  return 0;
}


/*-----------------------------------------------------------------*/
/* allocParms - parameters are always passed on stack              */
/*-----------------------------------------------------------------*/
void
allocParms (value * val)
{
  value *lval;
  int pNum = 1;

  for (lval = val; lval; lval = lval->next, pNum++)
  {
      /* mark it as my parameter */
      lval->sym->localof = currFunc;

      /* generate a unique name  */
      sprintf (lval->sym->rname,
                "%s%s_PARM_%d", "_", currFunc->name, pNum);
      strncpyz (lval->name,	 lval->sym->rname, sizeof(lval->name));
      SPEC_SCLS (lval->etype) = S_CODE;

	  /* alloc this variable on stack */
	  lval->sym->onStack = 1;
	  SPEC_OCLS (lval->sym->etype) = stack;		/* stack space */
	  SPEC_STAK(lval->sym->etype) = lval->sym->stack = (stackPtr + 1);
	  stackPtr += getSize(lval->sym->type);

	  allocIntoSeg (lval->sym);	/* alloc now */
  }

  return;
}

/*-----------------------------------------------------------------*/
/* allocIntoSeg - puts a symbol into a memory segment              */
/*-----------------------------------------------------------------*/
void
allocIntoSeg (symbol * sym)
{
	memmap *segment = SPEC_OCLS (sym->etype);
	assert(segment);
	addSet (&segment->syms, sym);
}

/*-----------------------------------------------------------------*/
/* deleteFromSeg - deletes a symbol from segment used when a var   */
/*                 first declared as "extern" then no extern       */
/*-----------------------------------------------------------------*/
void deleteFromSeg(symbol *sym)
{
	if (SPEC_OCLS(sym->etype))
	{
		memmap *segment = SPEC_OCLS (sym->etype);
		assert(segment);
		deleteSetItem(&segment->syms, sym);
	}
}


/*-----------------------------------------------------------------*/
/* defaultOClass - set the output segment based on SCLASS          */
/*-----------------------------------------------------------------*/
bool
defaultOClass (symbol * sym)
{
	switch (SPEC_SCLS (sym->etype))
	{
		case S_CODE:
			if (sym->_isparm)
				return FALSE;

			SPEC_OCLS(sym->etype) = code;
			break;
		case S_DATA:
		case S_LITERAL:
		case S_FIXED:
			SPEC_OCLS(sym->etype) = data;
			break;
		case S_STACK:
			SPEC_OCLS(sym->etype) = stack;
			break;	
		default:
			return FALSE;
	}
	return TRUE;
}

void viewSeg(FILE *fp, memmap *mem)
{
	set *p;
	if (!mem)
		return;

	fprintf(fp, "Segment: %s\n", mem->sname);
	p = mem->syms;
	fprintf(fp, "Item:\n");
	while (p)
	{
		fprintf(fp, " - '%s' type: %s\n", ((symbol *) p->item)->name, printLinkChain(((symbol *) p->item)->type));
		p = p->next;
	}
}

void viewStackSeg(FILE *fp)
{
	set *p;
	symbol *sym;
	memmap *mem = stack;

	if (!mem)
		return;

	fprintf(fp, "Segment: %s\n", mem->sname);
	p = mem->syms;
	fprintf(fp, "Item:\n");
	while (p)
	{
		sym = (symbol *) p->item;
		fprintf(fp, " - '%s', type: %s, on func '%s', stack offset %d\n", sym->name, printLinkChain(sym->type), sym->localof->name, sym->stack);
		p = p->next;
	}
}

void fprintMemToFile(char *file)
{
	FILE *fp;
	fp = fopen(file, "wt");
	if (!fp)
		return;

	fprintf(fp, "************************** \n");	
	fprintf(fp, "* Memory allocation      * \n");	
	fprintf(fp, "************************** \n");	

	fprintf(fp, "\n");
	viewSeg(fp, data);

	fprintf(fp, "\n");
	viewSeg(fp, code);

	fprintf(fp, "\n");
	viewStackSeg(fp, stack);

	fclose(fp);
}

void fprintSymTabToFile(char *file)
{
	FILE *fp;
	fp = fopen(file, "wt");
	if (!fp)
		return;

	fprintf(fp, "*************************** \n");	
	fprintf(fp, "* Symbol Table Management * \n");	
	fprintf(fp, "*************************** \n");	

	fprintf(fp, "\nSymbol table");
	viewTable(fp, SymbolTab, 0, 255);

	fprintf(fp, "\nTypedef table");
	viewTable(fp, TypedefTab, 0, 255);

	fprintf(fp, "\nStructTab table");
	viewTable(fp, StructTab, 0, 255);

	fclose(fp);
}